(function(_global) {
    var RE_SPACE = /%20/g;
    _global.onAjaxError = function(xhr) {
	alert("Ajax Error:" + xhr.statusText + "(" + xhr.status + ")");
    }
    X.Object.extend({
	params: function() {
	    var params = [];
	    function buildParams(name, value) {
		if (value instanceof Array) {
		    X(value).each(function(val, index) {
			buildParams(name, val);
		    });
		} else {
		    name = String(name), value = String(value);
		    params.push(encodeURIComponent(name) + "=" + encodeURIComponent(value));
		}
	    }
	    this.each(function(value, name) {
		buildParams(name, value);
	    });
	    return params.join("&").replace(RE_SPACE, "+");
	}
    });
    X.String.extend({
	get: function(options) {
	    this.ajax("GET", options);
	},
	getSync: function(options) {
	    return this.sjax("GET", options);
	},
	post: function(options) {
	    this.ajax("POST", options);
	},
	postSync: function() {
	    return this.sjax("GET", options);
	},
	ajax: function(method, options) {
	    options = options || {};
	    var url = this.val();
	    var body = options.body;
	    var params = options.params;
	    if (!options.contentType) {
		if (params) {
		    options.contentType = "application/x-www-form-urlencoded";
		} else if (body) {
		    throw "When request body is raw string, you must set the contentType option";
		}
	    }
	    var onsuccess = options.success;
	    if (typeof onsuccess !== "function") {
	    	 throw new Error("success callback is required");
	    }
	    var onerror = options.error || _global.onAjaxError;

	    if (!body && params) {
		body = X(params).params();
	    }

	    var xhr = prepareHttpRequest(method, url, options);
	    xhr.onreadystatechange = function() {
		if (xhr.readyState !== 4) {
		    return;
		}
		if (isHttpSuccess(xhr.status)) {
		    onsuccess(xhr);
		} else {
		    onerror(xhr);
		}
	    };
	    if (body) {
		xhr.send(body);
	    } else {
		xhr.send("");
	    }
	},
	sjax: function(method, options) {
	    var url = this.val();
	    var xhr = prepareHttpRequest(method, url, options);
	    xhr.send();
	    if (isHttpSuccess(xhr.status)) {
		return xhr;
	    } else {
		var error = new Error(xhr.statusText);
		error.name = "HttpError";
		error.request = xhr;
		throw error;
	    }
	}
    });
    function prepareHttpRequest(method, url, options) {
	options = options || {};
	var user = options.user || undefined;
	var password = options.password || undefined;
	var headers = options.headers;

	var xhr = createHttpRequest();
	xhr.open(method, url, true, user, password);
	if (headers) {
	    X(headers).each(function(value, name) {
		xhr.setRequestHeader(String(name), String(value));
	    });
	}
	xhr.setRequestHeader("Content-Type", options.contentType);
	return xhr;
    }
    function createHttpRequest() {
	try {
            return new XMLHttpRequest();
	} catch (e) {
            try {
		return new ActiveXObject("Msxml2.XMLHTTP");
            } catch (e) {
		try {
                    return new ActiveXObject("Microsoft.XMLHTTP");
		} catch (e) {
		    throw e;
		}
            }
	}
    };
    function isHttpSuccess(status) {
	if (status >= 200 && status < 300) {
	    return true;
	}
	return false;
    }
})(this);
